﻿//------------------------------------------------------------------
// System Name:    NRemedy
// Component:      NRemedy
// Create by:      Parker Zhou (parkerz@wicresoft.com)
// Create Date:    2012-04-10
//------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using BMC.ARSystem;

namespace NRemedy
{
    /// <summary>
    /// ARProxy
    /// </summary>
    /// <example>
    ///string ARServer = ConfigurationManager.AppSettings["ARServer"];
    ///string ARUid = ConfigurationManager.AppSettings["ARUid"];
    ///string ARPwd = ConfigurationManager.AppSettings["ARPwd"];
    ///ARLoginContext context = new ARLoginContext(ARServer, ARUid, ARPwd);
    ///ARProxy&lt;Rack&gt;  rack = new ARProxy&lt;Rack&gt; (context);
    ///string querystr = string.Format("\'InstanceId\'=\"{0}\"", instanceId.Text);
    ///Rack ra = rack.GetEntryByQuery(querystr)[0];
    ///ra.Loaction="rack_Location"
    ///rack.SetEntry(ra); 
    /// </example>
    /// <typeparam name="T">Type</typeparam>
    public class ARProxy<T>
    {
        protected ARLoginContext loginContext;
        private static Object lockObject = new Object(); //保证对同一个Form，只有一种操作。

        /// <summary>
        /// Constructor 1 
        /// </summary>
        /// <!--William Wang-->
        /// <param name="context">LoginContext</param>
        /// <param name="factory">Server Factory</param>
        public ARProxy(ARLoginContext context, ARServerDefaultFactory factory)
        {
            if (context == null) throw new ArgumentNullException("context");
            if (string.IsNullOrEmpty(context.Server)) throw new ArgumentNullException("context.Server");
            if (string.IsNullOrEmpty(context.User)) throw new ArgumentNullException("context.User");
            IARServer server = factory.CreateARServer();
            context.Login(server);
            loginContext = context;
        }

        /// <summary>
        /// Constructor 2
        /// </summary>
        /// <!--William Wang-->
        /// <param name="context">LoginContext</param>
        public ARProxy(ARLoginContext context)
            : this(context, new ARServerDefaultFactory())
        {
        }

        /// <summary>
        /// Create AR Entry by model, and indicate modelbinder、FormNameUtil
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entry">model</param>
        /// <param name="binder">ModelBinder</param>
        /// <param name="getFormName">getFormName Delegate</param>
        public string CreateEntry(T entry, IModelBinder<T> binder, ARGetFormNameDelegate getFormName)
        {
            if (entry == null)
                throw new ArgumentNullException("entry");
            if (binder == null)
                throw new ArgumentNullException("binder");
            if (getFormName == null)
                throw new ArgumentNullException("getFormName");
            if (loginContext.LoginStatus != ARLoginStatus.Success || loginContext.ServerInstance == null)
                throw new UnLoginException();
            lock (lockObject)
            {
                return loginContext.ServerInstance.CreateEntry(getFormName(typeof(T)), binder.UnBind(entry));
            }
        }

        /// <summary>
        /// Create AR Entry by model, and indicate modelbinder、FormNameUtil
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entry">model</param>
        /// <param name="binder">ModelBinder</param>
        /// <param name="getFormName">getFormName Delegate</param>
        /// <param name="isLock">isLock</param>
        private void CreateEntry(T entry, IModelBinder<T> binder, ARGetFormNameDelegate getFormName, int isLock)
        {
            if (entry == null)
                throw new ArgumentNullException("entry");
            if (binder == null)
                throw new ArgumentNullException("binder");
            if (getFormName == null)
                throw new ArgumentNullException("getFormName");
            if (loginContext.LoginStatus != ARLoginStatus.Success || loginContext.ServerInstance == null)
                throw new UnLoginException();

            loginContext.ServerInstance.CreateEntry(getFormName(typeof(T)), binder.UnBind(entry));
        }

        /// <summary>
        /// Create AR Entry by model using default modelbinder、GetFormName 
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entry">model</param>
        public string CreateEntry(T entry)
        {
            return CreateEntry(entry, DefaultFactory.CreateModelBinder<T>(), GetFormName);
        }

        /// <summary>
        /// Create list entry
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entries">List Model</param>
        /// <param name="binder">ModelBinder</param>
        /// <param name="getFormName">getFormName</param>
        /// <param name="isEnableTransaction">is enable transaction</param>
        public void CreateListEntry(IList<T> entries, IModelBinder<T> binder, ARGetFormNameDelegate getFormName, bool isEnableTransaction = false)
        {
            if (entries == null || entries.Count == 0)
                throw new ArgumentNullException("entries");
            if (binder == null)
                throw new ArgumentNullException("binder");
            if (getFormName == null)
                throw new ArgumentNullException("getFormName");
            if (loginContext.LoginStatus != ARLoginStatus.Success || loginContext.ServerInstance == null)
                throw new UnLoginException();
            lock (lockObject)
            {
                try
                {
                    if (isEnableTransaction)
                    {
                        loginContext.ServerInstance.BeginBulkEntryTransaction();
                    }
                    foreach (T item in entries)
                    {
                        CreateEntry(item, binder, GetFormName, 2);
                    }
                    if (isEnableTransaction)
                    {
                        loginContext.ServerInstance.SendBulkEntryTransaction();
                    }
                }
                catch
                {
                    if (isEnableTransaction)
                    {
                        loginContext.ServerInstance.CancelBulkEntryTransaction();
                    }
                    throw;
                }
            }
        }

        /// <summary>
        /// Create list entry
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entries">List Model</param>
        /// <param name="isEnableTransaction">is enable transaction</param>
        public void CreateListEntry(IList<T> entries, bool isEnableTransaction = false)
        {
            if (entries == null || entries.Count == 0)
                throw new ArgumentNullException("entries");
            if (loginContext.LoginStatus != ARLoginStatus.Success || loginContext.ServerInstance == null)
                throw new UnLoginException();
            CreateListEntry(entries, DefaultFactory.CreateModelBinder<T>(), GetFormName, isEnableTransaction);
        }

        /// <summary>
        /// Delete AR Entry by model, and indicate modelbinder、FormNameUtil
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entry">model</param>
        /// <param name="getFormName">getFormName Delegate</param>
        public void DeleteEntry(T entry, ARGetFormNameDelegate getFormName)
        {
            if (entry == null)
                throw new ArgumentNullException("entry");
            if (getFormName == null)
                throw new ArgumentNullException("getFormName");
            if (loginContext.LoginStatus != ARLoginStatus.Success || loginContext.ServerInstance == null)
                throw new UnLoginException();
            lock (lockObject)
            {
                loginContext.ServerInstance.DeleteEntry(getFormName(typeof(T)), GetEntryId(entry), 0);
            }
        }

        /// <summary>
        /// Delete AR Entry by model, and indicate modelbinder、FormNameUtil
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entry">model</param>
        /// <param name="getFormName">getFormName Delegate</param>
        /// <param name="isLock">isLock</param>
        private void DeleteEntry(T entry, ARGetFormNameDelegate getFormName, int isLock)
        {
            if (entry == null)
                throw new ArgumentNullException("entry");
            if (getFormName == null)
                throw new ArgumentNullException("getFormName");
            if (loginContext.LoginStatus != ARLoginStatus.Success || loginContext.ServerInstance == null)
                throw new UnLoginException();

            loginContext.ServerInstance.DeleteEntry(getFormName(typeof(T)), GetEntryId(entry), 0);
        }

        /// <summary>
        /// Delete AR Entry by model using default modelbinder、GetFormName 
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entry">model</param>
        public void DeleteEntry(T entry)
        {
            DeleteEntry(entry, GetFormName);
        }

        /// <summary>
        /// Delete list entry
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entries">List Model</param>
        /// <param name="getFormName">getFormName</param>
        /// <param name="isEnableTransaction">is enable transaction</param>
        public void DeleteListEntry(IList<T> entries, ARGetFormNameDelegate getFormName, bool isEnableTransaction = false)
        {
            if (entries == null || entries.Count == 0)
                throw new ArgumentNullException("entries");
            if (getFormName == null)
                throw new ArgumentNullException("getFormName");
            if (loginContext.LoginStatus != ARLoginStatus.Success || loginContext.ServerInstance == null)
                throw new UnLoginException();
            lock (lockObject)
            {
                try
                {

                    if (isEnableTransaction)
                    {
                        loginContext.ServerInstance.BeginBulkEntryTransaction();
                    }
                    foreach (T item in entries)
                    {
                        DeleteEntry(item, GetFormName, 3);
                    }
                    if (isEnableTransaction)
                    {
                        loginContext.ServerInstance.SendBulkEntryTransaction();
                    }
                }
                catch
                {
                    if (isEnableTransaction)
                    {
                        loginContext.ServerInstance.CancelBulkEntryTransaction();
                    }
                    throw;
                }
            }
        }

        /// <summary>
        /// Delete list entry
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entries">List Model</param>
        /// <param name="isEnableTransaction">is enable transaction</param>
        public void DeleteListEntry(IList<T> entries, bool isEnableTransaction = false)
        {
            if (entries == null || entries.Count == 0)
                throw new ArgumentNullException("entries");
            if (loginContext.LoginStatus != ARLoginStatus.Success || loginContext.ServerInstance == null)
                throw new UnLoginException();
            DeleteListEntry(entries, GetFormName, isEnableTransaction);
        }

        /// <summary>
        /// Delete entris by query
        /// </summary>
        /// <!--William Wang-->
        /// <param name="qualification"> for example: "\'cChr_ProcessType\' LIKE \"%geGen%\" AND \'cInt_ApproveOrder\'=1004"  </param>
        /// <param name="getFormName">getFormName</param>
        /// <param name="isEnableTransaction">is enable transaction</param>
        /// <param name="useLocale">whether to use locale while getting the matched entry list </param>
        public void DeleteEntryByQuery(string qualification, ARGetFormNameDelegate getFormName,
            bool isEnableTransaction = false, bool useLocale = false)
        {
            if (String.IsNullOrEmpty(qualification))
                throw new ArgumentNullException("qualification");
            if (getFormName == null)
                throw new ArgumentNullException("getFormName");
            if (loginContext.LoginStatus != ARLoginStatus.Success || loginContext.ServerInstance == null)
                throw new UnLoginException();
            lock (lockObject)
            {
                try
                {

                    if (isEnableTransaction)
                    {
                        loginContext.ServerInstance.BeginBulkEntryTransaction();
                    }

                    foreach (EntryDescription item in loginContext.ServerInstance.GetListEntry(getFormName(typeof(T)), qualification))
                    {
                        EntryDescription des = (EntryDescription)item;
                        loginContext.ServerInstance.DeleteEntry(getFormName(typeof(T)), des.EntryId);
                    }

                    if (isEnableTransaction)
                    {
                        loginContext.ServerInstance.SendBulkEntryTransaction();
                    }
                }
                catch
                {
                    if (isEnableTransaction)
                    {
                        loginContext.ServerInstance.CancelBulkEntryTransaction();
                    }
                    throw;
                }
            }

        }


        /// <summary>
        /// Delete entris by query
        /// </summary>
        /// <!--William Wang-->
        /// <param name="qualification"> for example: "\'cChr_ProcessType\' LIKE \"%geGen%\" AND \'cInt_ApproveOrder\'=1004"  </param>
        /// <param name="isEnableTransaction">is enable transaction</param>
        /// <param name="useLocale">whether to use locale while getting the matched entry list </param>
        public void DeleteEntryByQuery(string qualification, bool isEnableTransaction = false, bool useLocale = false)
        {
            DeleteEntryByQuery(qualification, GetFormName, isEnableTransaction, useLocale);
        }

        /// <summary>
        /// Set AR Entry by model
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entry">model</param>
        /// <param name="binder">ModelBinder</param>
        /// <param name="getFormName">getFormName Delegate</param>
        public void SetEntry(T entry, IModelBinder<T> binder, ARGetFormNameDelegate getFormName)
        {
            if (entry == null)
                throw new ArgumentNullException("entry");
            if (binder == null)
                throw new ArgumentNullException("binder");
            if (getFormName == null)
                throw new ArgumentNullException("getFormName");
            if (loginContext.LoginStatus != ARLoginStatus.Success || loginContext.ServerInstance == null)
                throw new UnLoginException();
            lock (lockObject)
            {
                loginContext.ServerInstance.SetEntry(getFormName(typeof(T)), GetEntryId(entry), binder.UnBindForUpdate(entry));
            }
        }

        /// <summary>
        /// Set AR Entry by model
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entry">model</param>
        /// <param name="binder">ModelBinder</param>
        /// <param name="getFormName">getFormName Delegate</param>
        /// <param name="isLock">isLock</param>
        private void SetEntry(T entry, IModelBinder<T> binder, ARGetFormNameDelegate getFormName, int isLock)
        {
            if (entry == null)
                throw new ArgumentNullException("entry");
            if (binder == null)
                throw new ArgumentNullException("binder");
            if (getFormName == null)
                throw new ArgumentNullException("getFormName");
            if (loginContext.LoginStatus != ARLoginStatus.Success || loginContext.ServerInstance == null)
                throw new UnLoginException();

            loginContext.ServerInstance.SetEntry(getFormName(typeof(T)), GetEntryId(entry), binder.UnBindForUpdate(entry));
        }

        /// <summary>
        /// Set AR Entry by model using default modelbinder、default getFormName
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entry">model</param>
        public void SetEntry(T entry)
        {
            SetEntry(entry, DefaultFactory.CreateModelBinder<T>(), GetFormName);
        }

        /// <summary>
        /// Set list entry
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entries">List Model</param>
        /// <param name="binder">ModelBinder</param>
        /// <param name="getFormName">getFormName</param>
        /// <param name="isEnableTransaction">is enable transaction</param>
        public void SetListEntry(IList<T> entries, IModelBinder<T> binder, ARGetFormNameDelegate getFormName, bool isEnableTransaction = false)
        {
            if (entries == null || entries.Count == 0)
                throw new ArgumentNullException("entries");
            if (binder == null)
                throw new ArgumentNullException("binder");
            if (getFormName == null)
                throw new ArgumentNullException("getFormName");
            if (loginContext.LoginStatus != ARLoginStatus.Success || loginContext.ServerInstance == null)
                throw new UnLoginException();
            lock (lockObject)
            {
                try
                {

                    if (isEnableTransaction)
                    {
                        loginContext.ServerInstance.BeginBulkEntryTransaction();
                    }
                    foreach (T item in entries)
                    {
                        SetEntry(item, binder, GetFormName, 1);
                    }
                    if (isEnableTransaction)
                    {
                        loginContext.ServerInstance.SendBulkEntryTransaction();
                    }
                }
                catch
                {
                    if (isEnableTransaction)
                    {
                        loginContext.ServerInstance.CancelBulkEntryTransaction();
                    }
                    throw;
                }
            }
        }

        /// <summary>
        /// Set list entry
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entries">List Model</param>
        /// <param name="isEnableTransaction">is enable transaction</param>
        public void SetListEntry(IList<T> entries, bool isEnableTransaction = false)
        {
            if (entries == null || entries.Count == 0)
                throw new ArgumentNullException("entries");
            if (loginContext.LoginStatus != ARLoginStatus.Success || loginContext.ServerInstance == null)
                throw new UnLoginException();
            SetListEntry(entries, DefaultFactory.CreateModelBinder<T>(), GetFormName);
        }

        /// <summary>
        /// Set entris by query
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entry"> entry</param>
        /// <param name="qualification"> for example: "\'cChr_ProcessType\' LIKE \"%geGen%\" AND \'cInt_ApproveOrder\'=1004"  </param>
        /// <param name="binder">binder</param>
        /// <param name="getFormName">ARGetFormNameDelegate</param>
        /// <param name="isEnableTransaction">isEnableTransaction</param>
        /// <param name="noMatchOption">NoMatchOption</param>
        /// <param name="multiMatchOption">MultiMatchOption</param>
        /// <param name="useLocale">whether to use locale while getting the matched entry list </param>
        public void SetEntryByQuery(T entry, string qualification, IModelBinder<T> binder, ARGetFormNameDelegate getFormName,
            bool isEnableTransaction = false, Server.SetEntryByQuery_NoMatchOption noMatchOption = Server.SetEntryByQuery_NoMatchOption.NoAction,
            Server.SetEntryByQuery_MultiMatchOption multiMatchOption = Server.SetEntryByQuery_MultiMatchOption.ModifyAll, bool useLocale = false)
        {
            if (entry == null)
                throw new ArgumentNullException("entry");
            if (String.IsNullOrEmpty(qualification))
                throw new ArgumentNullException("qualification");
            if (binder == null)
                throw new ArgumentNullException("binder");
            if (getFormName == null)
                throw new ArgumentNullException("getFormName");
            if (loginContext.LoginStatus != ARLoginStatus.Success || loginContext.ServerInstance == null)
                throw new UnLoginException();
            lock (lockObject)
            {
                try
                {

                    if (isEnableTransaction)
                    {
                        loginContext.ServerInstance.BeginBulkEntryTransaction();
                    }

                    loginContext.ServerInstance.SetEntryByQuery(getFormName(typeof(T)), qualification,
                        noMatchOption, multiMatchOption, binder.UnBindForUpdate(entry), useLocale);

                    if (isEnableTransaction)
                    {
                        loginContext.ServerInstance.SendBulkEntryTransaction();
                    }
                }
                catch
                {
                    if (isEnableTransaction)
                    {
                        loginContext.ServerInstance.CancelBulkEntryTransaction();
                    }
                    throw;
                }
            }

        }

        /// <summary>
        /// Set entris by query
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entry"> entry</param>
        /// <param name="qualification"> for example: "\'cChr_ProcessType\' LIKE \"%geGen%\" AND \'cInt_ApproveOrder\'=1004"  </param>
        /// <param name="isEnableTransaction">isEnableTransaction</param>
        /// <param name="noMatchOption">NoMatchOption</param>
        /// <param name="multiMatchOption">MultiMatchOption</param>
        /// <param name="useLocale">whether to use locale while getting the matched entry list </param>
        public void SetEntryByQuery(T entry, string qualification,
            bool isEnableTransaction = false, Server.SetEntryByQuery_NoMatchOption noMatchOption = Server.SetEntryByQuery_NoMatchOption.NoAction,
            Server.SetEntryByQuery_MultiMatchOption multiMatchOption = Server.SetEntryByQuery_MultiMatchOption.ModifyAll, bool useLocale = false)
        {
            SetEntryByQuery(entry, qualification, DefaultFactory.CreateModelBinder<T>(), GetFormName, isEnableTransaction, noMatchOption, multiMatchOption, useLocale);
        }

        
//        public object GetEntry(string entryID, Type targetModelType, IModelBinder<T> binder, ARGetFormNameDelegate getFormName,)

        /// <summary>
        /// Get AR Entry by model, and indicate modelbinder、GetFormName
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entryID">entryID</param>
        /// <param name="binder">ModelBinder</param>
        /// <param name="getFormName">getFormName Delegate</param>
        public T GetEntry(string entryID, IModelBinder<T> binder, ARGetFormNameDelegate getFormName)
        {
            if (String.IsNullOrEmpty(entryID))
                throw new ArgumentNullException("entry");
            if (binder == null)
                throw new ArgumentNullException("binder");
            if (getFormName == null)
                throw new ArgumentNullException("getFormName");
            if (loginContext.LoginStatus != ARLoginStatus.Success || loginContext.ServerInstance == null)
                throw new UnLoginException();
            lock (lockObject)
            {
                return binder.Bind(loginContext.ServerInstance.GetEntry(getFormName(typeof(T)), entryID, null));
            }
        }

        /// <summary>
        /// Get AR Entry by model, and indicate modelbinder、GetFormName
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entryID">model</param>
        /// <param name="binder">ModelBinder</param>
        /// <param name="getFormName">getFormName Delegate</param>
        /// <param name="isLock">isLock</param>
        private T GetEntry(string entryID, IModelBinder<T> binder, ARGetFormNameDelegate getFormName, int isLock)
        {
            if (String.IsNullOrEmpty(entryID))
                throw new ArgumentNullException("entry");
            if (binder == null)
                throw new ArgumentNullException("binder");
            if (getFormName == null)
                throw new ArgumentNullException("getFormName");
            if (loginContext.LoginStatus != ARLoginStatus.Success || loginContext.ServerInstance == null)
                throw new UnLoginException();
            return binder.Bind(loginContext.ServerInstance.GetEntry(getFormName(typeof(T)), entryID,null));
        }

        /// <summary>
        /// Get AR Entry by model using default modelbinder、default GetFormName
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entryID">entryID</param>
        public T GetEntry(string entryID)
        {
            return GetEntry(entryID, DefaultFactory.CreateModelBinder<T>(), GetFormName);
        }

        /// <summary>
        /// Get entris by query
        /// </summary>
        /// <!--William Wang-->
        /// <param name="qualification"> for example: "\'cChr_ProcessType\' LIKE \"%geGen%\" AND \'cInt_ApproveOrder\'=1004"  </param>
        /// <param name="binder">ModelBinder</param>
        /// <param name="getFormName">getFormName Delegate</param>
        public IList<T> GetEntryByQuery(string qualification, IModelBinder<T> binder, ARGetFormNameDelegate getFormName)
        {
            //TODO : if String.IsNullOrEmpty(qualification), all list will be returned?
            //if (String.IsNullOrEmpty(qualification))
            //    throw new ArgumentNullException("qualification");
            if (binder == null)
                throw new ArgumentNullException("binder");
            if (getFormName == null)
                throw new ArgumentNullException("getFormName");
            if (loginContext.LoginStatus != ARLoginStatus.Success || loginContext.ServerInstance == null)
                throw new UnLoginException();

            ArrayList entryIDs = loginContext.ServerInstance.GetListEntry(getFormName(typeof(T)), qualification);
            List<T> listT = new List<T>();
            foreach (var item in entryIDs)
            {
                EntryDescription des = (EntryDescription)item;
                listT.Add(GetEntry(des.EntryId, binder, getFormName, 2));
            }
            return listT;
        }

        /// <summary>
        /// Get entris by query
        /// </summary>
        /// <!--William Wang-->
        /// <param name="qualification"> for example: "\'cChr_ProcessType\' LIKE \"%geGen%\" AND \'cInt_ApproveOrder\'=1004"  </param>
        public IList<T> GetEntryByQuery(string qualification)
        {
            return GetEntryByQuery(qualification, DefaultFactory.CreateModelBinder<T>(), GetFormName);
        }

        /// <summary>
        /// get the formname 
        /// </summary>
        /// <!--William Wang-->
        /// <returns> form name</returns>
        protected string GetFormName(Type t)
        {
            string name = string.Empty;
            var formAttribute = t.GetCustomAttributes(false).Where(a => a is ARFormAttribute).FirstOrDefault() as ARFormAttribute;
            if (formAttribute != null)
            {
                name = formAttribute.FormName;
            }
            return name;
        }

        /// <summary>
        /// get the entry ID
        /// </summary>
        /// <!--William Wang-->
        /// <param name="entity"> model object</param>
        /// <returns></returns>
        private string GetEntryId(T entity)
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }
            foreach (PropertyInfo item in typeof(T).GetProperties())
            {
                if (item.GetCustomAttributes(false).Where(a => a is AREntryKeyAttribute).Count() != 0)
                {
                    return item.GetValue(entity, null).ToString();
                }
            }
            return string.Empty;
        }

    }

}
